/**
 * @export
 * @class Marker
 * @classdesc 气泡类
 * @property {Object} 地图容器 - SchoolLab实例
 * @property {String} name - 名称
 * @property {String} color - 颜色
 * @property {Number} opacity - 透明度
 * @property {Number} radius - 半径
 * @property {Number} renderOrder - 层叠顺序
 * @property {Object} data - 气泡数据
 * @property {Object} userData - 私有数据存储
 * @property {Object} building - 对应building对象
 * @property {Function} click - 点击事件回调
 * @example
 * var marker = new Marker(viewer, {
 *   radius: 4,
 *   color: '#ff0000',
 *   opacity: 0.5,
 *   renderOrder: 20,
 *   name: 'marker',
 *   data: {},
 *   userData: {},
 *   click: function() {},
 *   building: building,
 * })
 */
import { extend, lngLatToWorld, transformCoordinate, clamp } from './util'
// import html2canvas from '@/libs/html2canvas'
import html2canvas from 'html2canvas'
import Guid from './Guid'
export default class Marker {
  constructor(viewer, options) {
    const opts = extend({
      name: "marker",
      color: "#ffffff",
      opacity: 1,
      radius: 40,
      renderOrder: 1
    }, options)


    /** @member {SchoolLab} - 地图容器 */
    this.viewer = viewer
    
    /** @member {String} - 名称 */
    this.name = opts.name
    
    /** @member {Color} - 颜色 */
    this.color = opts.color
    
    /** @member {Number} - 透明度 */
    this.opacity = opts.opacity
    
    /** @member {Number} - 半径 */
    this.radius = opts.radius
    
    /** @member {Object} - 气泡数据 */
    this.data = Object.assign({}, opts.data)
    
    /** @member {Number} - 层叠顺序 */
    this.renderOrder = opts.renderOrder
    
    /** @member {THREE.Mesh} - 气泡对象 */
    this.entity = null
    
    /** @member {THREE.Mesh} - 扩散对象 */
    this.pulse = null
    
    /** @member {THREE.Mesh} - 扩散围栏对象 */
    this.wall = null
    
    /** @member {THREE.Object3D} - 对象存储容器 */
    this.outer = new THREE.Object3D()
    
    /** @member {Dom} - 气泡dom结构 */
    this.dom = null

    /** @member {Object} - 气泡私有数据 */
    this.userData = opts.userData

    /** @member {Building} - 气泡对应楼层对象 */
    this.building = opts.building
    
    /** @member {Function} - 点击事件回调 */
    this.click = opts.click
    
    /** @member {String} - 唯一id标识 */
    this.id = Guid.create()

    this._init()
  }

  // 初始化组件
  _init() {
    this.viewer.animationManager.regist(`marker_${this.id}`, this._update.bind(this))

    let dom = document.createElement('div')
    dom.className = `school-lab__marker ${this.data.status}`
    dom.id = `school-lab__marker_${this.data.buildingId}`

    let title = document.createElement('span')
    title.className = "school-lab__marker-title"
    title.id = `school-lab__marker-title_${this.data.buildingId}`
    title.innerHTML = this.data.buildingName
    dom.appendChild(title)

    let detail = document.createElement('ul')

    let classes = ['lab', 'chemical', 'gas']
    for(let i = 0; i < 3; i++) {
      let li = document.createElement('li')
      let icon = document.createElement('span')
      icon.className = `school-lab__marker-icon school-lab__marker-icon-${classes[i]}`
      icon.id = `school-lab__marker-icon-${classes[i]}`

      let desc = document.createElement('span')
      desc.className = 'school-lab__marker-detail'
      desc.id = `school-lab__marker-desc_${this.data.buildingId}-${classes[i]}`
      desc.innerHTML = this.data[`${classes[i]}Num`]
      li.appendChild(icon)
      li.appendChild(desc)

      detail.appendChild(li)
    }
    dom.appendChild(detail)

    document.body.appendChild(dom)
    this.dom = dom

    html2canvas(this.dom, {
      backgroundColor: null,
      // backgroundColor: 'rgba(0,0,0,0)',
      scale: 1,
    }).then(canvas => {
      const map = new THREE.Texture(canvas)
      map.minFilter = map.magFilter = THREE.LinearFilter
      
      const geometry = new THREE.PlaneBufferGeometry(90, 57.6, 2)
      const material = new THREE.MeshBasicMaterial({
        map: map,
        opacity: this.opacity,
        transparent: true,
        // depthWrite: false,
        depthTest: true,
        blending: THREE.NormalBlending,
        side: THREE.FrontSide,
      })
      material.map.needsUpdate = true
      const mesh = new THREE.Mesh(geometry, material)
      mesh.name = this.name
      mesh.renderOrder = this.renderOrder
      mesh.position.set(0, 45, 0)

      if (this.userData) {
        mesh.userData = this.userData
      }
      if (this.click) {
        mesh.click = this.click
      }
      if (this.building) {
        mesh.building = this.building
      }

      this.entity = mesh
      this.outer.add(mesh)

      canvas = null
    })

    this._initPulse()
    this._initWall()
  }

  // 初始化扩散组件
  _initPulse() {
    const geometry = new THREE.PlaneBufferGeometry(this.radius, this.radius, 2)

    let vs = this._initVS()
    let fs = this._initPulseFS()
    let color = this._getColor()

    const uniforms = {
      time: { type: "f", value: 0 },
      color: { type: "c", value: new THREE.Color(color) },
      opacity: { type: "f", value: this.opacity }
    }

    const material = new THREE.ShaderMaterial({
      uniforms: uniforms,
      vertexShader: vs,
      fragmentShader: fs,
      blending: THREE.AdditiveBlending,
      transparent: true,
      depthWrite: false,
      depthTest: true,
      side: THREE.DoubleSide
    })

    let mesh = new THREE.Mesh(geometry, material)
    mesh.name = this.name
    mesh.renderOrder = this.renderOrder
    this.outer.add(mesh)
    this.pulse = mesh
    if (this.data.status === 'normal') {
      this.pulse.visible = false
    }

    let world = lngLatToWorld(this.data.buildingLocation)
    let format = transformCoordinate(this.viewer.gisCenter, world)
    this.outer.position.set(format.x, format.y, 60)
    this.outer.renderOrder = this.renderOrder

    this.viewer.world.add(this.outer)
  }

  // 初始化vertexShader
  _initVS() {
    const vs = `
      precision lowp float;
      precision lowp int;
      
      varying vec2 vUv;
      void main() {
        vUv = uv;
        gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
      }
    `
    return vs
  }

  // 初始化pulse的fragmentShader
  _initPulseFS() {
    const fs = `
      precision lowp float;
      precision lowp int;
      varying vec2 vUv;
      uniform float time;
      uniform vec3 color;
      uniform float opacity;
      
      vec4 circle(vec2 uv, float rad) {
        float d = length(uv) - rad;
        float t = 1.0 - clamp(d, 0.0, 1.0);
        t = step(1., t);
        float dist = (rad) - length(uv);
        dist = clamp(dist * 1.35, 0.0, 1.0);
        t -= dist;
        return vec4(t);
      }
      void main() {
        vec2 uv = vUv - 0.5;
      
        float f_time = fract(time * 0.5);
        float f_time2 = fract((time + 1.) * 0.5);
      
        float radius = f_time * 0.5;
        float radius2 = f_time2 * 0.5;
      
        vec4 col = circle(uv, radius);
        vec4 col2 = circle(uv, radius2);
        col *= 1.0 - f_time;
        col2 *= 1.0 - f_time2;
      
        gl_FragColor = vec4(col.rgb + col2.rgb, 1.) * vec4(color, opacity);
      }
    `
    return fs
  }

  // 获取当前状态下的颜色配置
  _getColor() {
    let color = ''
    switch (this.data.status) {
      case 'red':
        color = '#F63253'
        break
      case 'orange':
        color = '#F3732F'
        break
      case 'normal':
        color = '#61A8C1'
        break
      case 'yellow':
        color = '#F6D501'
        break
      default:
        console.log('status: ', this.data.status)
    }
    return color
  }

  // 初始化扩散围栏
  _initWall() {
    const geometry = new THREE.CylinderBufferGeometry(100, 100, 70, 64)

    const loader = new THREE.TextureLoader()
    const map = loader.load(require('../../assets/images/linear.png'))

    let color = this._getColor()

    const sideMaterial = new THREE.MeshBasicMaterial({
      opacity: this.opacity,
      transparent: true,
      depthWrite: false,
      depthTest: true,
      side: THREE.DoubleSide,
      blending: THREE.AdditiveBlending,
      map: map,
      color: new THREE.Color(color)
    })
    sideMaterial.map.needsUpdate = true
    sideMaterial.needsUpdate = true

    const topMaterial = new THREE.MeshBasicMaterial({
      opacity: 0,
      transparent: true,
      depthWrite: false,
      depthTest: true,
      side: THREE.DoubleSide,
      map: map,
      color: new THREE.Color("#000000")
    })

    const bottomMaterial = new THREE.MeshBasicMaterial({
      opacity: 0,
      transparent: true,
      depthWrite: false,
      depthTest: true,
      side: THREE.DoubleSide,
      map: map,
      color: new THREE.Color("#000000")
    })
    let materials =  [sideMaterial, topMaterial, bottomMaterial]

    const mesh = new THREE.Mesh(geometry, materials)
    mesh.rotation.x = Math.PI / 2
    mesh.scale.set(0.0001, 1, 0.0001)
    mesh.name = this.name
    mesh.renderOrder = this.renderOrder

    let world = lngLatToWorld(this.data.buildingLocation)
    let format = transformCoordinate(this.viewer.gisCenter, world)
    mesh.position.set(format.x, format.y, 50)
    mesh.visible = this.data.status !== 'normal'
    this.wall = mesh
    this.viewer.world.add(mesh)
  }

  // 更新气泡数据
  updateData(data) {
    // console.log('this.data', this.data, data)
    if (!this._compareObject(data, this.data)) {
      let box = document.getElementById(`school-lab__marker_${this.data.buildingId}`)
      box.className = `school-lab__marker ${data.status}`
  
      let title = document.getElementById(`school-lab__marker-title_${this.data.buildingId}`)
      title.innerHTML = data.buildingName
  
      let classes = ['lab', 'chemical', 'gas']
      for(let i = 0; i < 3; i++) {
        let desc = document.getElementById(`school-lab__marker-desc_${this.data.buildingId}-${classes[i]}`)
        desc.innerHTML = data[`${classes[i]}Num`]
      }
  
      html2canvas(this.dom, {
        backgroundColor: null,
        scale: 1,
      }).then(canvas => {
        const map = new THREE.Texture(canvas)
        map.minFilter = map.magFilter = THREE.LinearFilter
        this.entity.material.map = map
        this.entity.material.map.needsUpdate = true
        canvas = null
      })

      this.pulse.visible = data.status !== 'normal'
      this.wall.visible = data.status !== 'normal'

      let color = this._getColor()
      this.pulse.material.uniforms.color.value = new THREE.Color(color)
      this.wall.material[0].color = new THREE.Color(color)
    }
    
    this.data = Object.assign({}, data)
  }

  // 更新动画状态
  _update() {
    if (this.outer) {
      this.outer.quaternion.copy(this.viewer.camera.quaternion)
    }
    if (this.pulse) {
      this.pulse.material.uniforms.time.value += 0.01
    }
    if (this.wall) {
      this.wall.scale.set((this.wall.scale.x + 0.008) % 1, 1, (this.wall.scale.z + 0.008) % 1)
      this.wall.material[0].opacity = this._calcOpacity(clamp(1 - this.wall.scale.x, 0, 1))
    }
  }

  _calcOpacity(opacity) {
    return opacity > 0.85 ? 1 : opacity * (1 / 0.85)
  }

  // 数据差量对比
  _compareObject(data1, data2) {
    let keys = ["status", "labNum", "chemicalNum", "gasNum"]
    let status = true
    for (let i = 0; i < keys.length; i++) {
      if (data1[keys[i]] !== data2[keys[i]]) {
        status = false
      }
    }

    return status
  }

  // 销毁组件
  destroy() {
    this._disposeHierarchy(this.pulse, this._dispose)
    this.viewer.world.remove(this.pulse)
    this.pulse = null
    
    this._disposeHierarchy(this.wall, this._dispose)
    this.viewer.world.remove(this.wall)
    this.wall = null
    
    this._disposeHierarchy(this.outer, this._dispose)
    this.viewer.world.remove(this.outer)
    this.outer = null
    
    this.dom = null
    this.viewer.animationManager.unload(`marker_${this.id}`)
  }

  // 控制当前marker组件显隐状态
  setVisible(state) {
    this.entity.visible = state
    this.pulse.visible = this.data.status !== 'normal' && state
    this.wall.visible = this.data.status !== 'normal' && state
  }
}
